/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#pragma once

#include <string>
#include <map>
#include <memory>

// export
namespace Zen
{
  template <typename TKey, typename Data>
  class Cache;
}

namespace Zen
{
  template <typename TKey, typename Data>
  class Cache
  {
public:
    Cache() = default;

    virtual ~Cache() = default;

    std::shared_ptr<Data> At(TKey const& key)
    {
      auto iter = m_cache.find(key);
      return (iter == m_cache.end()) ? nullptr : iter->second;
    }

    std::shared_ptr<Data> GetShared(TKey const& key)
    {
      auto iter = m_cache.find(key);
      if (iter != m_cache.end())
      {
        return iter->second;
      }
      auto value = LoadData(key);
      if (value)
      {
        m_cache[key] = value;
      }
      return value;
    }

    void Insert(TKey const& key, std::shared_ptr<Data> v)
    {
      if (v == nullptr)
        m_cache.erase(key);
      else
        m_cache[key] = v;
    }

    void Clear(bool force = false)
    {
      if (force)
      {
        m_cache.clear();
      }
      else
      {
        for (auto iter = m_cache.begin(); iter != m_cache.end();)
        {
          if (iter->second.use_count() > 1)
            ++iter;
          else
            iter = m_cache.erase(iter);
        }
      }
    }

protected:
    virtual std::shared_ptr<Data> LoadData(TKey const& key) // override this.
    {
      return nullptr;
    }

protected:
    std::map<TKey, std::shared_ptr<Data>> m_cache;
  };
} // namespace Zen
